# 基础节点访问器的类
from interpreters.base_node_visitor import NodeVisitor
from symbol.base_symbol import Symbol,BuiltinTypeSymbol,VarSymbol
# 符号表
from symbol.symbol_table import SymbolTable
# 作用域符号表
from symbol.scoped_symbol_table import ScopedSymbolTable
# 调用栈 , 活动记录
from interpreters.call_stack_and_activation_records import CallStack,ActivationRecord,ARType
#错误
from error.error import syntax_error,type_error

# 语义分析器
class SemanticAnalyzer(NodeVisitor):
	def __init__(self):
		self.symbol_table = SymbolTable();
		# 作用域符号表
		# self.scope = ScopedSymbolTable(scope_name='global', scope_level=1);
		self.current_scope = None  # 初始化保存符号表的变量
		# 调用堆栈
		self.call_stack = CallStack()

	def visit_Program(self, node):
		print('>>> 进入作用域：global')
		# 创建全局作用域符号表
		# global_scope = ScopedSymbolTable(scope_name='global', scope_level=1,enclosing_scope=self.current_scope)
		# 创建全局作用域符号表
		global_scope = ScopedSymbolTable(scope_name='global', scope_level=1)
		global_scope._init_builtins()  # 初始化内置类型
		self.current_scope = global_scope
		self.visit(node.block)
		print(global_scope)
		print(global_scope)

		# self.current_scope = self.current_scope.enclosing_scope  # 离开作用域时设置当前作用域为外围作用域
		print('<<< 离开作用域：global')

	def visit_Block(self, node):
		for declaration in node.declarations:
			self.visit(declaration)
		self.visit(node.compound_statement)

	#定义声明变量
	def visit_VarDecl(self, node):
		#变量类型名称存到符号表中
		symbol_type = node.type_node.name
		self.symbol_table.lookup(symbol_type)
		#变量名称存到符号表中
		var_name = node.var_node.name

		# 查询变量名称，如果存在变量信息
		if self.symbol_table.lookup(var_name) is not None:
			# 抛出异常
			raise Exception(f'错误：发现重复的标识符：{var_name}')

		var_symbol = VarSymbol(var_name, symbol_type)
		self.symbol_table.insert(var_symbol)

	def visit_Compound(self, node):
		for child in node.children:
			self.visit(child)

	def visit_NoOperator(self, node):
		pass

	def visit_Variable(self, node):
		# pass
		var_name = node.name
		var_symbol = self.symbol_table.lookup(var_name)
		if var_symbol is None:
			syntax_error(var_name,'变量没有定义');

	def visit_BinaryOperator(self, node):
		self.visit(node.left)
		self.visit(node.right)

	def visit_ComparisonOperation(self,node):
		pass

	def visit_Num(self, node):
		pass

	# 访问字符串节点
	def visit_Str(self,node):
		pass

	def visit_Assign(self, node):
		self.visit(node.left)
		self.visit(node.right)

	# 访问加法赋值语句节点
	def visit_PlusAssign(self,node):
		pass

	def visit_MinusAssign(self,node):
		pass
	# 访问乘法赋值语句节点
	def visit_MulAssign(self,node):
		pass
	# 访问除法赋值语句节点
	def visit_DivAssign(self,node):
		pass

	def visit_If(self, node):
		self.visit(node.condition)
		self.visit(node.then_node)
		if node.else_node == "":
			pass
		else:
			self.visit(node.else_node)
	def visit_For(self, node):
		self.visit(node.start_node)
		self.visit(node.end_node)
		self.visit(node.do_node)

	# 添加访问函数声明的方法
	def visit_Function(self, node):
		proc_name = node.name  # 获取过程节点名称
		# proc_symbol = ProcedureSymbol(proc_name)  # 创建过程符号
		# self.current_scope.insert(proc_symbol)  # 过程符号添加到当前作用域
		print(f'>>> 进入作用域：{proc_name}')  # 显示输出进入过程作用域
		# proc_scope = ScopedSymbolTable(scope_name=proc_name, scope_level=self.current_scope.scope_level + 1,enclosing_scope=self.current_scope)  # 创建过程的作用域符号表
		# self.current_scope = proc_scope  # 当前作用域设置为过程作用域
		for param in node.params:  # 遍历过程的形式参数列表
			print('visit_Function:',param.type_node.name)
			param_type = self.current_scope.lookup(param.type_node.name)  # 获取参数类型
			param_name = param.var_node.name  # 获取参数名称
			param_symbol = VarSymbol(param_name, param_type)  # 创建参数符号
			self.current_scope.insert(param_symbol)  # 将参数符号添加到当前作用域
			# proc_symbol.formal_params.append(param_symbol)  # 为过程符号参数列表添加参数符号
		self.visit(node.block_node)  # 访问过程中的块节点
		# print(proc_scope)  # 显示输出过程作用域符号表信息
		# self.current_scope = self.current_scope.enclosing_scope  # 离开作用域时设置当前作用域为外围作用域
		print(f'<<< 离开作用域：{proc_name}')  # 显示输出离开过程作用域

		# 把过程声明下面的Block节点作为过程符号的block_ast成员。在过程调用的时候，解释器需要访问它
		# proc_symbol.block_ast = node.block_node

	#　函数调用遍历方法
	def visit_Call(self, node):
		#遍历调用所有的实参列表
		for param_node in node.actual_params:
			#但由于最底层的visit_Num等是空，所以这里的遍历暂时没有什么意义
			self.visit(param_node)
		proc_name = node.proc_name
		# #从当前域中查询到过程符号
		# proc_symbol = self.current_scope.lookup(proc_name)
		# #把查询到的过程符号放到当前过程调用节点下，在后面的解释器interpreter执行过程调用的时候要用到它
		# node.proc_symbol = proc_symbol





	def visit_Print(self, node):
		for node in node.params:
			self.visit(node)
	def visit_Length(self, node):
		pass

	#获取变量类型
	def visit_GetType(self, node):
		pass
